#include <assert.h>

#include "Point.h"
#include "Point_r.h"

/* ----------------------------------------------------------------------------------------------- *
 *	POINT : реализация методов
 */

static void * Point_ctor(void * _pSelf, va_list * ppArgs)
{
	// переопределённый конструктор

	// вызываем конструктор базового типа OBJECT
	struct Point * pSelf = base_ctor(Point, _pSelf, ppArgs);

	// инициализируем точку
	pSelf->x = va_arg( *ppArgs, int );
	pSelf->y = va_arg( *ppArgs, int );

	return pSelf;
}

static void Point_draw(const void * _pSelf)
{
	// рисование точки (отсутствует у предка)

	const struct Point * pSelf = _pSelf;

	printf("\".\" at %d,%d\n", pSelf->x, pSelf->y);
}


/* ----------------------------------------------------------------------------------------------- *
 *	POINT_CLASS : реализация методов
 */

// PointClass_ctor() -- инициализация структуры с мета-информацией о типе

static void * PointClass_ctor(void * _pSelf, va_list * ppArgs)
{
	// переопределённая инициализация мета-информации о типе POINT

	// вызываем конструктор базового типа OBJECT_CLASS
	POINT_CLASS * pSelf = base_ctor(PointClass, _pSelf, ppArgs);

	// завершаем инициализацию, переопределение методов

	PFUNC pfnSelector = NULL; // селектор -- функция динамически связанная с методом POINT
	PFUNC pfnMethod = NULL;   // новая реализация метода POINT
	va_list pArgs = * ppArgs;

	while ( 1 )
	{
		pfnSelector = va_arg( pArgs, PFUNC ); // аргумент 5 + 2i - селектор

		if ( !pfnSelector )
			break; // конец списка переопределений

		pfnMethod = va_arg( pArgs, PFUNC );   // аргумент 5 + 2i+1 - новая реализация метода

		if ( pfnSelector == (PFUNC) draw )
			* (PFUNC *) &(pSelf->pfnDraw) = pfnMethod;
	}

	return pSelf;
}


/* ----------------------------------------------------------------------------------------------- *
 *	объявление переменных с мета-информацией о типах POINT и POINT_CLASS
 */

static OBJECT_CLASS point_class_info; // мета-информация о типе POINT_CLASS
static POINT_CLASS  point_info;       // мета-информация о типе POINT

/* ----------------------------------------------------------------------------------------------- *
 *	инициализация "красивых" констант-указателей на мета-информацию о POINT и POINT_CLASS
 */

const void * PointClass = &point_class_info;
const void * Point      = &point_info;

/* ----------------------------------------------------------------------------------------------- *
 *	автоматическая инициализация структур с мета-информацией об POINT и POINT_CLASS
 */

void init_PointClass(void)
{
	inplace_new(
		&point_class_info, // мета-информация о типе POINT_CLASS
		ObjectClass,       // мета-информация является экземпляром типа OBJECT_CLASS

		"PointClass",        // символическое имя типа
		ObjectClass,         // мета-информация о предке (предок POINT_CLASS -> OBJECT_CLASS)
		sizeof(POINT_CLASS), // размер типа в байтах

		ctor, PointClass_ctor, // переопределено динамическое связывание ctor->PointClass_ctor
		0
	);

	inplace_new(
		&point_info, // мета-информация о типе POINT
		PointClass,  // мета-информация является экземпляром типа POINT_CLASS

		"Point",       // символическое имя типа
		Object,        // мета-информация о предке (предок POINT -> OBJECT)
		sizeof(POINT), // размер типа в байтах

		ctor, Point_ctor, // переопределено динамическое связывание ctor -> Point_ctor
		draw, Point_draw, // установлено динамическое связывание    draw -> Point_draw
		0
	);
}


/* ----------------------------------------------------------------------------------------------- *
 *	реализация функций с динамическим связыванием (dynamic linking)
 *		динамическое связывание 1 метода для типа POINT и его наследников
 */

void draw(const void * _pSelf)
{
	const POINT_CLASS * pClass = classOf(_pSelf);
	assert( pClass->pfnDraw );
	pClass->pfnDraw(_pSelf);
}


/* ----------------------------------------------------------------------------------------------- *
 *	вспомогательная функция вызывающие методы базового типа
 *		допустим вызов этой функции для наследников POINT
 */

void base_draw(const void * _pClass, const void * _pSelf)
{
	const POINT_CLASS * pBaseClass = baseOf(_pClass);
	assert( _pSelf && pBaseClass->pfnDraw );
	pBaseClass->pfnDraw(_pSelf);
}


/* ----------------------------------------------------------------------------------------------- *
 *	реализация функций со статическим связыванием (static linking)
 *		статическое связывание 1 функции для типа POINT и его наследников
 */

void move(void * _pSelf, int dx, int dy)
{
	POINT * pSelf = _pSelf;
	pSelf->x += dx;
	pSelf->y += dy;
}

